<template>
  <!-- 1.设计阶段 -->
  <multipane :style="{ height }" class="x-render">
    <!-- 左侧 -->
    <slot name="left" :renderComponents="sortedRenderComponents" :xRenderForm="this">
      <x-render-left/>
    </slot>

    <multipane-resizer/>
    <!-- 中间 -->
    <slot name="main" :xRender="this">
      <x-render-main @save="saveHandler"/>
    </slot>

    <template v-if="rightTabs.length !== 0">
      <multipane-resizer/>
      <!-- 右侧 -->
      <slot name="right" :xRenderForm="this">
        <x-render-right/>
      </slot>
    </template>
  </multipane>
</template>

<script>
// 组件
import XRenderLeft from './components/form/left/index'
import XRenderMain from './components/form/main/index'
import XRenderRight from './components/form/right/index'
import Multipane from './components/multipane/Multipane'
import MultipaneResizer from './components/multipane/MultipaneResizer'

// 工具函数
import {cloneDeep, isPlainObject, omitBy} from 'lodash'

import serialize from 'serialize-javascript'
import {COMPONENT_TYPE} from './libs/constants'
import {getFormOption} from './libs/utils'
// 组件的数据，通过 provide / inject 的形式传递给子组件
// 主要原因是因为组件嵌套过深，相当于一个状态管理
export default {
  name: 'XForm',
  components: {
    Multipane,
    MultipaneResizer,
    XRenderLeft,
    XRenderRight,
    XRenderMain
  },
  inheritAttrs: false,
  provide() {
    return {
      xDesignForm: this
    }
  },
  props: {
    // 表单内容
    config: {
      type: String,
      default() {
        return ''
      }
    },
    // 设计器整体高度
    height: {
      type: [String, Number],
      default: '400px'
    },
    // 保存格式
    saveFormat: {
      type: String,
      default: 'string',
      validator(val) {
        return ['object', 'string'].includes(val)
      }
    },
    // 默认属性
    formProps: {
      type: Object,
      default: () => {
      }
    },
    // 组件列表
    comps: {
      type: Array,
      default: () => []
    },
    // 操作配置
    operations: {
      type: Array,
      default: () => ['preview', 'data', 'code', 'batch', 'clear', 'save']
    },
    // 右侧属性面板 Tabs
    rightTabs: {
      type: Array,
      default: () => [
        {
          label: '表单项', name: 'x-design-form-item-common', show: (xDesignForm) => {
            const currentFormOption = xDesignForm.getCurrentFormOption()
            return !!currentFormOption && !currentFormOption.isLayout
          }
        },
        {label: '组件栅格', name: 'x-design-form-layout', show: true},
        {label: '组件属性', name: 'x-design-form-item-attrs', show: true},
        {label: '表单配置', name: 'x-design-form-props', show: true}
      ],
      validator(tabs) {
        return (tabs || []).every(item => item.label && item.name)
      }
    },
    // 表单相关（designer 为 true 时）
    value: {
      type: Object,
      default: () => {
        return {}
      }
    },
    // 主界面进行loading
    loading: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      // 表单数据
      formData: {},
      // 定义表单属性数据
      formPropsData: {},
      // 表单项数组形态
      formOptions: [],
      // 当前 formOption值
      currentId: '',
      currentOption: null
    }
  },
  computed: {
    customRenderComponents: {
      get() {
        const {comps} = this
        return cloneDeep(comps).filter(item => !!item.baseInfo && !!item.template && !!item.attrs && !!item.component)
      }
    },
    renderComponents: {
      get() {
        const {customRenderComponents} = this
        const arr = cloneDeep([...this.$XRenderComponentInfo]).filter(item => !!item.baseInfo && !!item.template && !!item.attrs && !!item.component)
        return [...arr, ...customRenderComponents]
      }
    },
    // 属性文档的 Map 形式
    attrUrlMap: {
      get() {
        const {sortedRenderComponents} = this
        const map = {}
        cloneDeep(sortedRenderComponents)
          .map((item) => {
            const baseInfo = item.baseInfo
            map[baseInfo.code] = baseInfo.url || '#'

          })
        return map
      }
    },
    // 属性的 Map 形式
    attrsMap: {
      get() {
        const {sortedRenderComponents} = this
        const map = {}
        cloneDeep(sortedRenderComponents)
          .map((item) => {
            const baseInfo = item.baseInfo
            map[baseInfo.code] = item.attrs || []
          })
        return map
      }
    },
    // 组件模板的 Map 形式
    templatesMap: {
      get() {
        const {sortedRenderComponents} = this
        const map = {}
        cloneDeep(sortedRenderComponents)
          .map((item) => {
            const baseInfo = item.baseInfo
            map[baseInfo.code] = item.template || {}
          })
        return map
      }
    },
    // 排序后的组件
    sortedRenderComponents: {
      get() {
        const {renderComponents} = this
        return cloneDeep(renderComponents)
          .filter(item => !!item.baseInfo.sort)
          .sort((a, b) => a.baseInfo.sort - b.baseInfo.sort)
      }
    }
  },
  watch: {
    config: {
      immediate: true,
      deep: true,
      handler(config) {
        // 判断是否有值
        if (!config) return
        let formConfig = cloneDeep(config)
        // 如果是字符串，则转为对象
        if (typeof config === 'string') {
          try {
            // 有可能解析出错
            formConfig = formConfig ? (new Function('return ' + formConfig))() : {}
            if (!isPlainObject(formConfig)) {
              // 如果不是对象，则也抛出遗产
              throw new TypeError('config 不是对象')
            }
          } catch (err) {
            this.$message.error('数据解析失败')
            console.error(err)
            return
          }
        }

        if (!isPlainObject(formConfig)) {
          // 如果不是对象，则也抛出异常
          console.error('[x-render]: config 不是对象', formConfig)
          return
        }

        const {formProps, formOptions} = cloneDeep(formConfig)
        // 赋值操作
        this.formPropsData = Object.assign(this.formPropsData, formProps)
        this.formOptions.splice(0, this.formOptions.length, ...formOptions)

        // 当有数据时，选中第一个
        if (this.formOptions.length !== 0) {
          this.currentId = this.formOptions[0]['id']
        }
      }
    },
    currentId: {
      immediate: true,
      deep: true,
      handler() {
        this.getCurrentFormOption()
      }
    }
  },
  created() {
    // 设置表单属性默认值
    this.initFormPropsData()
    // 防止火狐拖拽打开新标签
    this.ondrop = function (event) {
      event.preventDefault()
      event.stopPropagation()
    }

    document.body.addEventListener('drop', this.ondrop)
  },
  beforeDestroy() {
    document.body.removeEventListener('drop', this.ondrop)
  },
  methods: {
    // 获取当前配置项
    getCurrentFormOption() {
      const {formOptions, currentId} = this
      if (currentId) {
        return getFormOption(formOptions, currentId)
      }
      return {}
    },
    /**
     * 组件名称
     *
     * @param type {string} 组件名称
     * @returns {string} 设计组件名称
     */
    getComponent(type) {
      const {renderComponents} = this
      const item = renderComponents.find(({baseInfo}) => {
        return baseInfo.code === type
      })
      if (item) {
        if (item.design) {
          return item.design
        }
        if (item.component) {
          return item.component
        }
      }
      return COMPONENT_TYPE.ERROR
    },
    /**
     * 移除无用的属性
     */
    removeEmptyProps(obj = {}) {
      return omitBy(obj, val => val === undefined || val === null || val === '')
    },
    // 保存数据
    saveHandler() {
      // 根据用户传入的保存格式进行判断
      if (this.saveFormat === 'string') {
        this.$emit('save', this.getFormConfigStr())
      } else {
        this.$emit('save', this.getFormConfig())
      }
    },
    // 获取序列化后的表单配置
    getFormConfigStr() {
      return serialize(this.getFormConfig(), {space: 2})
    },
    // 获取表单配置
    getFormConfig() {
      return {
        // 表单属性
        formProps: this.removeEmptyProps(cloneDeep(this.formPropsData)),
        // 表单项
        formOptions: cloneDeep(this.formOptions)
      }
    },
    // 设置表单属性默认值
    initFormPropsData() {
      this.formPropsData = Object.assign(
        this.formPropsData,
        cloneDeep(this.formProps)
      )
    }
  }
}
</script>

<style lang="css" scoped>
/* x-render 整体样式 */
.x-render {
  border: 1px solid #ebeef5;
  overflow: hidden;
}

.x-render-form {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  display: flex;
  flex-flow: column nowrap;
  padding: 5px 15px;
}

/* 左右面板拖动的抓手 */
.x-render > .multipane-resizer {
  margin: 0;
  left: 0;
  position: relative;
  box-sizing: border-box;
  border-left: 1px solid #ebeef5;
  border-right: 1px solid #ebeef5;
  width: 7px;
}

.x-render > .multipane-resizer::before {
  display: block;
  content: "";
  width: 1px;
  height: 40px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -20px;
  margin-left: -1.5px;
  box-sizing: border-box;
  border-left: 1px solid #dcdfe6;
  border-right: 1px solid #dcdfe6;
}

.x-render > .multipane-resizer::before::before {
  border-color: #999;
}
</style>
